package com.sunwayworld.basemodule.rm.material.service.impl;

import com.sunwayworld.basemodule.common.constant.LimsConstant;
import com.sunwayworld.basemodule.rm.material.bean.LimsMaterialBean;
import com.sunwayworld.basemodule.rm.material.bean.LimsStandardHistoryBean;
import com.sunwayworld.basemodule.rm.material.bean.LimsStandardInventoryBean;
import com.sunwayworld.basemodule.rm.material.dao.LimsStandardInventoryDao;
import com.sunwayworld.basemodule.rm.material.service.LimsMaterialService;
import com.sunwayworld.basemodule.rm.material.service.LimsStandardHistoryService;
import com.sunwayworld.basemodule.rm.material.service.LimsStandardInventoryService;
import com.sunwayworld.basemodule.rm.materialapply.bean.LimsMaterialOutBean;
import com.sunwayworld.basemodule.rm.materialapply.service.LimsMaterialOutService;
import com.sunwayworld.framework.at.annotation.AuditTrailEntry;
import com.sunwayworld.framework.at.annotation.AuditTrailType;
import com.sunwayworld.framework.audit.aunnotation.Audit;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.context.LocalContextHelper;
import com.sunwayworld.framework.exception.InvalidDataException;
import com.sunwayworld.framework.i18n.I18nHelper;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.restful.data.RestJsonWrapperBean;
import com.sunwayworld.framework.spring.annotation.GikamBean;
import com.sunwayworld.framework.support.PersistableMetadataHelper;
import com.sunwayworld.framework.utils.ArrayUtils;
import com.sunwayworld.framework.utils.NumberUtils;
import com.sunwayworld.framework.utils.StringUtils;
import com.sunwayworld.module.sys.metadata.service.CoreTableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@GikamBean
public class LimsStandardInventoryServiceImpl implements LimsStandardInventoryService {

    @Autowired
    private LimsStandardInventoryDao limsStandardInventoryDao;

    @Autowired
    @Lazy
    private LimsStandardHistoryService limsStandardHistoryService;

    @Lazy
    @Autowired
    private LimsMaterialService limsMaterialService;

    @Lazy
    @Autowired
    private LimsMaterialOutService limsMaterialOutService;

    @Override
    @SuppressWarnings("unchecked")
    public LimsStandardInventoryDao getDao() {
        return limsStandardInventoryDao;
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.INSERT)
    public Long insert(RestJsonWrapperBean jsonWrapper) {
        LimsStandardInventoryBean limsStandardInventory = jsonWrapper.parseUnique(LimsStandardInventoryBean.class);
        limsStandardInventory.setId(ApplicationContextHelper.getNextIdentity());
        limsStandardInventory.setOperator(LocalContextHelper.getLoginUserName());
        limsStandardInventory.setOperatorId(LocalContextHelper.getLoginUserId());

        limsStandardInventory.setStorageBatch(ApplicationContextHelper.getNextSequence(getDao().getTable().concat("_STORAGEBATCH"), limsStandardInventory.getMatCode().concat("-")));

        LimsMaterialBean materialBean = limsMaterialService.selectFirstByFilter(SearchFilter.instance().match("matCode", limsStandardInventory.getMatCode()).filter(MatchPattern.EQ));
        if (materialBean != null && materialBean.getId() != null) {
            double matNum = materialBean.getMatNum() == null ? 0.0 : materialBean.getMatNum();
            materialBean.setMatNum(matNum + limsStandardInventory.getStorageNum());
            limsMaterialService.getDao().update(materialBean, "matNum");
            saveHistory(limsStandardInventory, limsStandardInventory.getStorageNum(), limsStandardInventory.getSupplier(),
                    LimsConstant.MATERIAL_OPERATION_ADD);
        }

        getDao().insert(limsStandardInventory);
        return limsStandardInventory.getId();
    }

    @Transactional
    @AuditTrailEntry(AuditTrailType.DELETE)
    @Audit("AUDIT.DELETE")
    public void delete(RestJsonWrapperBean wrapper) {
        List<LimsStandardInventoryBean> deleteList = wrapper.parse(this.getDao().getType());
        if (!deleteList.isEmpty()) {
            List<Long> inventoryIdList = deleteList.stream().map(LimsStandardInventoryBean::getId).collect(Collectors.toList());
            List<LimsStandardHistoryBean> historyBeanList = limsStandardHistoryService
                    .selectListByFilter(SearchFilter.instance().match("STANDARDINVENTORYID", inventoryIdList).filter(MatchPattern.OR));
            if (historyBeanList.stream().anyMatch(e -> !StringUtils.equals(e.getOperation(), "入库"))) {
                throw new InvalidDataException("库存存在出库或盘存的情况，无法删除");
            }
            deleteList = selectListByFilter(SearchFilter.instance().match("ID", inventoryIdList).filter(MatchPattern.OR));
            LimsStandardInventoryBean inventoryBean = deleteList.get(0);
            LimsMaterialBean materialBean = limsMaterialService.selectFirstByFilter(SearchFilter.instance().match("matCode", inventoryBean.getMatCode()).filter(MatchPattern.EQ));
            deleteList.forEach(item -> {
                materialBean.setMatNum(materialBean.getMatNum() - item.getStorageNum());
            });
            // 删除操作记录
            limsStandardHistoryService.getDao().deleteBy(historyBeanList);
            // 删除批次
            Class<LimsStandardInventoryBean> itemType = this.getDao().getType();
            CoreTableService tableService = ApplicationContextHelper.getBean(CoreTableService.class);
            deleteList.forEach((t) -> {
                tableService.deleteCascade(PersistableMetadataHelper.getTableName(itemType), (Serializable) t.getId());
            });
            // 修改库存总量
            limsMaterialService.getDao().update(materialBean, LimsConstant.MATERIAL_PROP_MATNUM);
        }
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.UPDATE)
    public void saveHistory(LimsStandardInventoryBean standardInventoryBean, Double inlibrarynum, String supplier, String operation) {
        LimsStandardHistoryBean standardInventoryHistoryBean = new LimsStandardHistoryBean();
        standardInventoryHistoryBean.setStandardInventoryId(standardInventoryBean.getId());
        standardInventoryHistoryBean.setBatchNo(standardInventoryBean.getStorageBatch());
        standardInventoryHistoryBean.setQuantity(inlibrarynum);
//        standardInventoryHistoryBean.setSupplier(supplier);
//        standardInventoryHistoryBean.setUnit(specificationsUnit);
//        standardInventoryHistoryBean.setDescribe(standardInventoryBean.getRemark());
        standardInventoryHistoryBean.setId(ApplicationContextHelper.getNextSequenceNumber(getDao().getTable()));
        standardInventoryHistoryBean.setOperation(operation);
        standardInventoryHistoryBean.setCreatedByName(LocalContextHelper.getLoginUser().getUserName());
        standardInventoryHistoryBean.setCreatedByOrgName(LocalContextHelper.getLoginUser().getOrgName());
        standardInventoryHistoryBean.setAuditStatus("完成");
        standardInventoryHistoryBean.setCreatedTime(LocalDateTime.now());
        standardInventoryHistoryBean.setCompleteTime(standardInventoryHistoryBean.getCreatedTime());
        limsStandardHistoryService.getDao().insert(standardInventoryHistoryBean);
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.UPDATE)
    public void checkOutStandard(RestJsonWrapperBean jsonWrapper) {
        LimsStandardInventoryBean fromInventoryBean = jsonWrapper.parse(LimsStandardInventoryBean.class, 0).get(0);
        LimsStandardInventoryBean standardInventoryBean = jsonWrapper.parse(LimsStandardInventoryBean.class, 1).get(0);

        // 判断是否是材料管理出库
        if (StringUtils.equals("yes", standardInventoryBean.getExt$Item("isoutoperation"))) {
            standardInventoryBean.setId(fromInventoryBean.getId());
            // 出库数量
            Double outNum = NumberUtils.parseDouble(standardInventoryBean.getExt$Item("outNum"));
            String supplier = standardInventoryBean.getSupplier();
            String issueType = standardInventoryBean.getExt$Item("issueType");
            standardInventoryBean = getDao().selectById(standardInventoryBean.getId());
            // 查询matCode对应的材料配置
            LimsMaterialBean materialBean = limsMaterialService.selectFirstByFilter(SearchFilter.instance().match("matCode", fromInventoryBean.getMatCode()).filter(MatchPattern.EQ));
            if (materialBean == null) {
                return;
            }
            if (standardInventoryBean.getBatchNum() - outNum < 0) {
                //超出入库单的批次库存  不能出库
                throw new InvalidDataException(I18nHelper.getMessage("BASEMODULE.RM.MATERIALS.TIP.OUTNUM.TOOBIG"));
            }

            // 出库后的数量
            standardInventoryBean.setBatchNum(standardInventoryBean.getBatchNum() - outNum);
            // 历史记录表增加记录
            LimsStandardHistoryBean standardHistoryBean = getHistory(standardInventoryBean, outNum, supplier, LimsConstant.MATERIAL_OPERATION_TAKE);
            standardHistoryBean.setAuditStatus("完成");
            standardHistoryBean.setCompleteTime(standardHistoryBean.getCreatedTime());
            standardHistoryBean.setIssueType(issueType);
            limsStandardHistoryService.getDao().insert(standardHistoryBean);
            // 库存表更新数据
            getDao().update(standardInventoryBean, "batchNum");
            // 更新T_MATERIAL表总量
            materialBean.setMatNum(materialBean.getMatNum() - outNum);
            limsMaterialService.getDao().update(materialBean, "matNum");
        } else {
            LimsStandardHistoryBean standardHistoryBean = new LimsStandardHistoryBean();
            // 出库数量
            Double outNum = NumberUtils.parseDouble(standardInventoryBean.getExt$Item("outNum"));
            String supplier = standardInventoryBean.getSupplier();
            // 查询matCode对应的材料配置
            LimsMaterialBean materialBean = limsMaterialService.selectFirstByFilter(SearchFilter.instance().match("matCode", fromInventoryBean.getMatCode()).filter(MatchPattern.EQ));
            if (materialBean == null) {
                return;
            }
/*            LimsStandardHistoryBean historyBean = limsStandardHistoryService.getDao().selectFirstIfPresent(standardHistoryBean, ArrayUtils.asList("id"));
            if (historyBean != null) {
                throw new InvalidDataException("该入库单已出库");
            }*/

            if (fromInventoryBean.getBatchNum() - outNum < 0) {
                //超出入库单的批次库存  不能出库
                throw new InvalidDataException(I18nHelper.getMessage("BASEMODULE.RM.MATERIALS.TIP.OUTNUM.TOOBIG"));
            }

            standardHistoryBean = getHistory(fromInventoryBean, outNum, supplier, LimsConstant.MATERIAL_OPERATION_TAKE);
            standardHistoryBean.setApplyId(Long.parseLong(jsonWrapper.getParamValue("applyId")));
            standardHistoryBean.setStandardInventoryId(fromInventoryBean.getId());
            String applyType = jsonWrapper.getParamValue("applyType");
            standardHistoryBean.setIssueType(applyType);
            limsStandardHistoryService.getDao().insert(standardHistoryBean);
            // 计算预出库数量
            LimsMaterialOutBean searchMaterialOut = new LimsMaterialOutBean();
            searchMaterialOut.setMatCode(fromInventoryBean.getMatCode());
            LimsMaterialOutBean materialOutBean = limsMaterialOutService.getDao().selectFirstIfPresent(searchMaterialOut);
            if (materialOutBean != null) {
                double preOutNum = materialOutBean.getPreOutNum() != null ? materialOutBean.getPreOutNum() : 0D;
                materialOutBean.setPreOutNum(preOutNum + standardHistoryBean.getQuantity());
                limsMaterialOutService.getDao().update(materialOutBean, "preOutNum");
            }
        }
    }

    @Override
    public LimsStandardHistoryBean getHistory(LimsStandardInventoryBean standardInventoryBean, Double inlibrarynum, String supplier, String operation) {
        LimsStandardHistoryBean standardInventoryHistoryBean = new LimsStandardHistoryBean();
        standardInventoryHistoryBean.setStandardInventoryId(standardInventoryBean.getId());
        standardInventoryHistoryBean.setBatchNo(standardInventoryBean.getStorageBatch());
        standardInventoryHistoryBean.setQuantity(inlibrarynum);
        standardInventoryHistoryBean.setId(ApplicationContextHelper.getNextSequenceNumber(getDao().getTable()));
        standardInventoryHistoryBean.setOperation(operation);
        standardInventoryHistoryBean.setCreatedByName(LocalContextHelper.getLoginUser().getUserName());
        standardInventoryHistoryBean.setCreatedByOrgName(LocalContextHelper.getLoginUser().getOrgName());
        standardInventoryHistoryBean.setCreatedTime(LocalDateTime.now());
        return standardInventoryHistoryBean;
    }

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.UPDATE)
    public void inventoryStandard(RestJsonWrapperBean jsonWrapper) {
        LimsStandardInventoryBean fromInventoryBean = jsonWrapper.parse(LimsStandardInventoryBean.class, 0).get(0);
        LimsStandardInventoryBean standardInventoryBean = jsonWrapper.parse(LimsStandardInventoryBean.class, 1).get(0);
        standardInventoryBean.setId(fromInventoryBean.getId());
        String difference = standardInventoryBean.getDifference();
        Double differenceNum = standardInventoryBean.getDifferenceNum();
        String remark = standardInventoryBean.getRemark();
        // 查询matCode对应的材料配置
        LimsMaterialBean materialBean = limsMaterialService.selectFirstByFilter(SearchFilter.instance().match("matCode", fromInventoryBean.getMatCode()).filter(MatchPattern.EQ));
        if (materialBean == null) {
            return;
        }
        standardInventoryBean = getDao().selectById(standardInventoryBean.getId());
        standardInventoryBean.setRemark(remark);
        String operation = "";
        if (LimsConstant.MATERIAL_FLAG_INVENTORY_ADD.equalsIgnoreCase(difference)) {
            standardInventoryBean.setBatchNum(standardInventoryBean.getBatchNum() + differenceNum);
            operation = LimsConstant.MATERIAL_OPERATION_INVENTORY_ADD;
        } else {
            standardInventoryBean.setBatchNum(standardInventoryBean.getBatchNum() - differenceNum);
            operation = LimsConstant.MATERIAL_OPERATION_INVENTORY_SUBTRACT;

            if (standardInventoryBean.getBatchNum() < 0) {
                throw new InvalidDataException("批次库存数量不足！");
            }
        }
        // 历史记录表增加记录
        saveHistory(standardInventoryBean, differenceNum, standardInventoryBean.getSupplier(), operation);
        // 库存表更新数据
        getDao().update(standardInventoryBean, "batchNum");
        // 更新T_MATERIAL表总量
        updateLimsMaterial(materialBean, differenceNum, operation);
    }

    @Override
    public void updateLimsMaterial(LimsMaterialBean materialBean, Double inlibrarynum, String operation) {
        Double matNum = materialBean.getMatNum();
        if (null == matNum)
            matNum = 0.0;
        if (LimsConstant.MATERIAL_OPERATION_ADD.equalsIgnoreCase(operation) || LimsConstant.MATERIAL_OPERATION_INVENTORY_ADD.equalsIgnoreCase(operation)) {
            materialBean.setMatNum(matNum + inlibrarynum);
        } else if (LimsConstant.MATERIAL_OPERATION_TAKE.equalsIgnoreCase(operation) || LimsConstant.MATERIAL_OPERATION_INVENTORY_SUBTRACT.equalsIgnoreCase(operation)) {
            materialBean.setMatNum(matNum - inlibrarynum);
        }
        limsMaterialService.getDao().update(materialBean, LimsConstant.MATERIAL_PROP_MATNUM);
    }

    @Override
    public void deleteHistory(RestJsonWrapperBean wrapper) {
        // 出库单审核，一次只会删除一个库存下的记录
        List<LimsStandardHistoryBean> standardHistoryBeanList = wrapper.parse(LimsStandardHistoryBean.class);
        List<Long> historyIds = standardHistoryBeanList.stream().map(LimsStandardHistoryBean::getId).collect(Collectors.toList());
        List<LimsStandardHistoryBean> standardHistoryBeans = limsStandardHistoryService.selectListByIds(historyIds);
        List<Long> inventoryIds = standardHistoryBeans.stream().map(LimsStandardHistoryBean::getStandardInventoryId).distinct().collect(Collectors.toList());
        LimsStandardInventoryBean materialStorageBean = selectById(inventoryIds.get(0));
        // 计算预出库数量
        LimsMaterialOutBean searchMaterialOut = new LimsMaterialOutBean();
        searchMaterialOut.setApplyId(NumberUtils.parseLong(standardHistoryBeans.get(0).getStandardInventoryId()));
        searchMaterialOut.setMatCode(materialStorageBean.getMatCode());
        LimsMaterialOutBean materialOutBean = limsMaterialOutService.getDao().selectFirstIfPresent(searchMaterialOut);
        if (materialOutBean != null) {
            double preOutNum = materialOutBean.getPreOutNum() != null ? materialOutBean.getPreOutNum() : 0D;
            materialOutBean.setPreOutNum(preOutNum - standardHistoryBeans.stream().filter(h -> h.getQuantity() != null).mapToDouble(LimsStandardHistoryBean::getQuantity).sum());
            limsMaterialOutService.getDao().update(materialOutBean, "preOutNum");
        }
        limsStandardHistoryService.getDao().deleteBy(standardHistoryBeans);
    }
}
